using System;
using System.Drawing;
using System.Windows.Forms;
using CombatTracker.Creatures;

namespace CombatTracker.Controls
{
  public struct LineCoordinates
  {
    public int X;
    public int Y;
    public int Width;
  }

  public partial class CombatControlScreen : UserControl
  {
    private InitiativeList _turnOrder;
    private CreatureList _delayedOrReadied;
    private CreatureList _deadOrUnconscious;

    public InitiativeList TurnOrder
    {
      set
      {
        _turnOrder = value;
        _turnOrder.TurnAdvanced += ChangeCurrentTurn;
        _turnOrder.ListChanged += RefreshTurnList;
        _turnOrder.RoundAdvanced += UpdateRound;
        _turnOrder.StartCombat();
        RefreshTurnList();
      }
    }

    private void UpdateRound(InitiativeList sender, Creature previousTurn)
    {
      lblRound.Text = sender.CurrentRound.ToString();
    }

    public CombatControlScreen()
    {
      InitializeComponent();

      _delayedOrReadied = new CreatureList();
      _delayedOrReadied.ListChanged += RefreshDelayedList;

      _deadOrUnconscious = new CreatureList();
      _deadOrUnconscious.ListChanged += RefreshDeadList;
    }

    private void RefreshDeadList()
    {
      RefreshDeadList(_deadOrUnconscious);
    }

    private void RefreshDeadList(CreatureList sender)
    {
      lsvDead.Items.Clear();

      foreach (Creature creature in sender)
      {
        ListViewItem lviNewItem = new ListViewItem(creature.Name);
        lviNewItem.Tag = creature;
        lsvDead.Items.Add(lviNewItem);
      }
    }

    private void RefreshDelayedList()
    {
      RefreshDelayedList(_delayedOrReadied);
    }

    private void RefreshDelayedList(CreatureList sender)
    {
      lsvDelayed.Items.Clear();

      foreach (Creature creature in sender)
      {
        ListViewItem lviNewItem = new ListViewItem(creature.Name);
        lviNewItem.Tag = creature;
        lsvDelayed.Items.Add(lviNewItem);
      }
    }

    private void RefreshTurnList()
    {
      RefreshTurnList(_turnOrder);
    }

    private void RefreshTurnList(InitiativeList sender)
    {
      lsvTurnOrder.Items.Clear();

      foreach (Creature creature in sender)
      {
        ListViewItem lviNewItem = new ListViewItem(creature.Name);
        lviNewItem.Tag = creature;
        lsvTurnOrder.Items.Add(lviNewItem);
      }
    }

    private void ChangeCurrentTurn(InitiativeList sender, Creature previousTurn)
    {
      lblCurrentTurn.Text = sender.CurrentTurn.Name;
    }

    protected override bool ProcessDialogKey(Keys keyData)
    {
      try
      {
        switch (keyData)
        {
          case Keys.Control | Keys.A:
            AdvanceTurn();
            break;
        }
        return base.ProcessDialogKey(keyData);
      }
      catch (Exception)
      {
        return false;
      }
    }

    private void btnAdvanceTurn_Click(object sender, EventArgs e)
    {
      AdvanceTurn();
    }

    private void AdvanceTurn()
    {
      if (_turnOrder != null &
          MessageBox.Show("Are you sure you wish to advance to the next turn?", "Next Turn", MessageBoxButtons.YesNo,
                          MessageBoxIcon.Question) == DialogResult.Yes)
      {
        _turnOrder.NextTurn();
      }
    }

    private void OnItemDrag(object sender, ItemDragEventArgs e)
    {
      ListView eventSender = (ListView) sender;
      ListViewItem[] items = new ListViewItem[eventSender.SelectedItems.Count];
      int counter = 0;
      foreach (ListViewItem item in eventSender.SelectedItems)
      {
        items[counter] = item;
        counter += 1;
      }
      eventSender.DoDragDrop(new DataObject(items), DragDropEffects.Move);
    }

    private void OnDragEnter(object sender, DragEventArgs e)
    {
      e.Effect = DragDropEffects.Move;
    }

    private void OnDragDrop(object sender, DragEventArgs e)
    {
      if (e.Data.GetDataPresent(typeof (ListViewItem[])))
      {
        ListViewItem[] draggedItems = (ListViewItem[]) e.Data.GetData(typeof (ListViewItem[]));
        RemoveItems(draggedItems);
        AddItems((ListView) sender, draggedItems);
      }
    }

    private void AddItems(ListView view, ListViewItem[] items)
    {
      switch (view.Name)
      {
        case "lsvTurnOrder":
          if (((CombatListView) view).HoverItem != null)
          {
            _turnOrder.AddBefore(_turnOrder.Find((Creature) ((CombatListView) view).HoverItem.Tag),
                                 (Creature) items[0].Tag);

            for (int i = 1; i < items.Length; i++)
            {
              _turnOrder.AddAfter(_turnOrder.Find((Creature) items[i - 1].Tag), (Creature) items[i].Tag);
            }
          }
          else
          {
            foreach (ListViewItem item in items)
            {
              _turnOrder.AddLast((Creature) item.Tag);
            }
          }
          break;
        case "lsvDelayed":
          foreach (ListViewItem item in items)
          {
            _delayedOrReadied.Add((Creature) item.Tag);
          }
          break;
        case "lsvDead":
          foreach (ListViewItem item in items)
          {
            _deadOrUnconscious.Add((Creature) item.Tag);
          }
          break;
        default:
          throw new ArgumentOutOfRangeException();
          break;
      }
    }

    private void RemoveItems(ListViewItem[] items)
    {
      switch (items[0].ListView.Name)
      {
        case "lsvTurnOrder":
          foreach (ListViewItem item in items)
          {
            _turnOrder.Remove((Creature) item.Tag);
          }
          break;
        case "lsvDelayed":
          foreach (ListViewItem item in items)
          {
            _delayedOrReadied.Remove((Creature) item.Tag);
          }
          break;
        case "lsvDead":
          foreach (ListViewItem item in items)
          {
            _deadOrUnconscious.Remove((Creature) item.Tag);
          }
          break;
        default:
          throw new ArgumentOutOfRangeException();
          break;
      }
    }
  }
}